/**
*
Java Diagram Package; An extremely flexible and fast multipurpose diagram
component for Swing.
Copyright (C) 2001 Eric Crahen <crahen@cse.buffalo.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package diagram.shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.NoSuchElementException;
/**
* @class PolyLine2D
*
* @date 08-20-2001
* @author Eric Crahen
* @version 1.0
*
* This class implements Shape, and
* consists of a series of straight-line segments.
*
* It allows segements to be added and split quickly, and
* is very efficent, minimizing the number of new objects
* created for most frequently used operations.
*
* Segment distance methods should be updated as well as
* the relativeCCW methods for a complete PolyLine2D implementation
*/
public abstract class PolyLine2D extends Line2D
implements Serializable {
protected int pointCount = 2;
/**
* Get an arbitrary point in this line
*/
abstract public Point2D getPN(int index);
/**
* Get the n'th Point2D in the line
*/
abstract public Point2D getPN(int index, Point2D pt);
/**
* Set an arbitrary point in this line
*/
public void setPN(int index, Point2D pt) {
setPN(index, pt.getX(), pt.getY());
}
/**
* Set an arbitrary point in this line
*/
abstract public void setPN(int index, double x, double y);
/**
* Get the x-value of the n'th point
*/
abstract public double getXN(int index);
/**
* Get the y-value of the n'th point
*/
abstract public double getYN(int index);
/**
* Count the Points in this line
*/
public int getPointCount() {
return pointCount;
}
/**
* Count the segments in this Line
*/
public int getSegmentCount() {
return (getPointCount() - 1);
}
/**
* Get the segment closest to the given point
*/
public int segmentFor(Point2D pt, double tolerance) {
return segmentFor(pt.getX(), pt.getY(), tolerance);
}
/**
* Get the segment closest to the given point
*/
abstract public int segmentFor(double x, double y, double tolerance);
/**
* Get the closest point in the line to another point
*/
abstract public int pointFor(double x, double y, double tolerance);
/**
* Get the boudns for this rectangle
*/
abstract public Rectangle2D getBounds2D(Rectangle2D rc);
/**
* Get the boudns for this rectangle
*/
public Rectangle2D getBounds2D() {
return getBounds2D(null);
}
/**
* See if this Point falls on or close to the line
*/
public boolean contains(Point2D pt) {
return contains(pt.getX(), pt.getY());
}
/**
* See if this Point falls on or close to the line
*/
public boolean contains(double x, double y) {
return contains(x, y, 3.0);
}
/**
* See if this Point falls on or close to the line
*/
public boolean contains(double x, double y, double tolerance) {
return segmentFor(x, y, tolerance) != -1;
}
/**
* Get the center of the bounding rectangle for this line.
*
* @param Point2D
* @return Point2D
*/
public Point2D getCenter(Point2D pt) {
double x1 = getX1(), x2 = getX2();
double y1 = getY1(), y2 = getY2();
double x = (x1 > x2) ? (x1-x2)/2.0 : (x2-x1)/2.0;
double y = (y1 > y2) ? (y1-y2)/2.0 : (y2-y1)/2.0;
if(pt == null)
return new Point2D.Double(x, y);
pt.setLocation(x, y);
return pt;
}
/**
* See if this line intersects the rectangle.
*/
public boolean intersects(double x, double y, double w, double h) {
// Implementation should NOT start by creating a new rectangle
throw new UnsupportedOperationException();
}
/**
* Translate the entire line
*/
abstract public void translate(double x, double y);
/**
* Split an existing segment into two bu inserting a point
*/
abstract public Point2D split(int segment, double x, double y);
/**
* Split an existing segment into two bu inserting a point
*/
public Point2D split(int segment, Point2D pt) {
return split(segment, pt.getX(), pt.getY());
}
/**
* Join together the two segments joined by a single point in the line
*/
abstract public void join(int point);
/**
*/
public PathIterator getPathIterator(AffineTransform at, double flatness) {
return getPathIterator(at);
}
/**
*/
public PathIterator getPathIterator(AffineTransform at) {
return new Iterator(at);
}
/**
* @class Double
*
*/
static public class Double extends PolyLine2D
implements Serializable {
protected Point2D.Double[] points;
public Double() {
points = new Point2D.Double[] { new Point2D.Double(0,0), new Point2D.Double(0,0) };
}
public Double(Point2D p1, Point2D p2) {
points[0].setLocation(p1);
points[1].setLocation(p2);
}
/**
* Set an arbitrary point in this line
*/
public void setPN(int index, double x, double y) {
if(index >= pointCount)
throw new NoSuchElementException("index out of bounds");
points[index].x = x;
points[index].y = y;
}
/**
* Get an arbitrary point in this line
*/
public Point2D getPN(int index) {
if(index >= pointCount)
throw new NoSuchElementException("index out of bounds");
return points[index];
}
/**
* Get an arbitrary point in this line
*/
public Point2D getPN(int index, Point2D pt) {
if(index >= pointCount)
throw new NoSuchElementException("index out of bounds");
if(pt == null)
return new Point2D.Double(points[index].x, points[index].y);
pt.setLocation(points[index]);
return pt;
}
public Point2D getP1() {
return points[0];
}
public Point2D getP2() {
return points[pointCount-1];
}
public double getXN(int index) {
if(index >= pointCount)
throw new NoSuchElementException("index out of bounds");
return points[index].x;
}
public double getYN(int index) {
if(index >= pointCount)
throw new NoSuchElementException("index out of bounds");
return points[index].y;
}
public double getX1() {
return points[0].x;
}
public double getY1() {
return points[0].y;
}
public double getX2() {
return points[pointCount-1].x;
}
public double getY2() {
return points[pointCount-1].y;
}
public Rectangle2D getBounds2D(Rectangle2D rc) {
double x1 = java.lang.Double.MAX_VALUE;
double y1 = java.lang.Double.MAX_VALUE;
double x2 = 0;
double y2 = 0;
for(int i = 0; i < pointCount; i++) {
double x = points[i].x;
double y = points[i].y;
if(x < x1)
x1 = x;
if(x > x2)
x2 = x;
if(y < y1)
y1 = y;
if(y > y2)
y2 = y;
}
if((x2-x1) < 0 || (y2-y1) < 0)
throw new RuntimeException("Bad Bounds2D [" + x1 + ", " + y1 + ", " + x2 + ", "+ y2 +"]");
if(rc == null)
return new Rectangle2D.Double(x1,y1,(x2-x1+1),(y2-y1+1));
rc.setRect(x1,y1,(x2-x1+1) ,(y2-y1+1));
return rc;
}
/**
* Get the segment closest to the given point
*/
public int segmentFor(double x, double y, double tolerance) {
double x1 = points[0].x, y1 = points[0].y;
double x2, y2;
int s = -1;
for(int i = 1; i < getPointCount(); i++) {
x2 = points[i].x;
y2 = points[i].y;
// Get the smallest distance to this point.
double dist;
if((dist = ptSegDist(x1, y1, x2, y2, x, y)) < tolerance) {
tolerance = dist;
s = (i-1);
}
x1 = x2;
y1 = y2;
}
// Return the closest segment
return s;
}
/**
* Get the closest point in the line to another point
*/
public int pointFor(double x, double y, double tolerance) {
int p = -1;
double dist = tolerance;
// Find the closest point
for(int i = 0; i < getPointCount(); i++) {
dist = Point2D.distanceSq(x, y, points[i].x, points[i].y);
if(dist < tolerance) {
tolerance = dist;
p = i;
}
}
return p;
}
/**
* Reset the line, removing all intermediate segments
*/
public void setLine(double X1, double Y1, double X2, double Y2) {
points[0].setLocation(X1, Y1);
points[1].setLocation(X2, Y2);
pointCount = 2;
}
/**
* Translate the entire line
*/
public void translate(double x, double y) {
for(int i=0; i < pointCount; i++) {
points[i].x += x;
points[i].y += y;
}
}
/**
* Split an existing segment into two
*/
public Point2D split(int segment, double x, double y) {
if(segment++ >= (pointCount - 1))
throw new NoSuchElementException("segment index out of bounds");
// expand the point array
if(pointCount == points.length) {
Point2D.Double[] temp = new Point2D.Double[pointCount*2];
System.arraycopy(points, 0, temp, 0, pointCount);
points = temp;
}
// Shift to the right
System.arraycopy(points, segment, points, (segment+1), pointCount-segment);
points[segment] = new Point2D.Double(x, y);
pointCount++;
return points[segment];
}
/**
* Join together the two segments joined by a single point in the line
*/
public void join(int point) {
if(point > (pointCount - 2) || point < 1)
throw new NoSuchElementException("point index out of bounds");
// Shift left
System.arraycopy(points, point+1, points, point, (pointCount-point-1));
pointCount--;
}
/**
* See if this line intersects the rectangle
*/
public boolean intersects(Rectangle2D rc) {
for(int i=0; i < pointCount-1; i++) {
if(rc.intersectsLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y))
return true;
}
return false;
}
private void writeObject(java.io.ObjectOutputStream out)
throws java.io.IOException {
out.writeInt(pointCount);
for(int i = 0; i < pointCount; i++) {
out.writeDouble(points[i].x);
out.writeDouble(points[i].y);
}
}
private void readObject(java.io.ObjectInputStream in)
throws java.io.IOException, ClassNotFoundException {
pointCount = in.readInt();
points = new Point2D.Double[pointCount];
for(int i = 0; i < pointCount; i++)
points[i] = new Point2D.Double(in.readDouble(), in.readDouble());
}
/**
* Implement a deep copy, rather than the default shallow copy
*/
public Object clone() {
// Clone all the internal points
Double link = (Double)super.clone();
link.points = new Point2D.Double[pointCount];
for(int i=0; i < pointCount; i++)
link.points[i] = new Point2D.Double(points[i].x, points[i].y);
return link;
}
} /* Double */
/**
* @class Iterator
*
*/
protected class Iterator implements PathIterator {
private AffineTransform affine;
private int index;
public Iterator(AffineTransform at) {
this.affine = at;
this.index = 0;
}
public int currentSegment(double coords[]) {
coords[0] = getXN(index);
coords[1] = getYN(index);
if(affine != null)
affine.transform(coords, 0, coords, 0, 1);
return (index == 0) ? SEG_MOVETO : SEG_LINETO;
}
public int currentSegment(float coords[]) {
coords[0] = (float)getXN(index);
coords[1] = (float)getYN(index);
if(affine != null)
affine.transform(coords, 0, coords, 0, 1);
return (index == 0) ? SEG_MOVETO : SEG_LINETO;
}
public int getWindingRule() {
return WIND_NON_ZERO;
}
public boolean isDone() {
return (index >= getPointCount());
}
public void next() {
index++;
}
} /* Iterator */
public String toString() {
return getClass() + "[" + getBounds() + "]@" + hashCode();
}
}